/*
 * Copyright (C) 2010-2014 Enrique Lara (enrique.posix2.lara@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
 * limitations under the License.
  */
package trs.gtfs;

import java.io.IOException;
import java.util.List;
import java.util.Set;

import trs.gtfs.model.Route;
import trs.gtfs.model.Stop;
import trs.gtfs.model.StopTime;
import trs.gtfs.model.Trip;
import trs.gtfs.model.TripTimePoint;
import trs.gtfs.parser.AgencyParser;
import trs.gtfs.parser.CalendarParser;
import trs.gtfs.parser.RouteParser;
import trs.gtfs.parser.StopParser;
import trs.gtfs.parser.StopTimeParser;
import trs.gtfs.parser.TripParser;
import trs.gtfs.predicate.RouteById;
import trs.gtfs.predicate.StopByCode;
import trs.gtfs.predicate.StopById;
import trs.gtfs.predicate.StopByIds;
import trs.gtfs.predicate.StopByLatLon;
import trs.gtfs.predicate.StopTimeByStopId;
import trs.gtfs.predicate.StopTimeByStopIdsAndTripIds;
import trs.gtfs.predicate.StopTimeByTripIds;
import trs.gtfs.predicate.StopTimeForStopsBeforeArrivalTime;
import trs.gtfs.predicate.StopTimeLaterInTrip;
import trs.gtfs.predicate.TripById;
import trs.gtfs.predicate.TripByRouteIds;

class Gtfs {
    private final String rootDir;
    private AgencyParser agencyParser = new AgencyParser();
    private TripParser tripParser = new TripParser();
    private StopParser stopParser = new StopParser();
    private RouteParser routeParser = new RouteParser();
    private CalendarParser calendarParser = new CalendarParser();
    private StopTimeParser stopTimeParser = new StopTimeParser();

    Gtfs(String rootDir) {
        this.rootDir = rootDir;
        try {
			load();
		} catch (IOException e) {
			throw new IllegalStateException("Could not initialize Gtfs.", e);
		}
    }

    public List<StopTime> getStopTimesByTripIds(Set<Long> tripIds) {
        return stopTimeParser.filter(stopTimeParser.getList(), new StopTimeByTripIds(tripIds));
    }

    public List<StopTime> getStopTimesAfter(StopTime stopTime) {
        return stopTimeParser.filter(stopTimeParser.getList(), new StopTimeLaterInTrip(stopTime.getTripId(), stopTime.getStopSequence()));
    }

    public List<StopTime> getStopTimesForStopsBefore(Set<Long> stopIds, TripTimePoint arrivalTime, int maxMinutesBeforehand) {
        return stopTimeParser.filter(stopTimeParser.getList(), new StopTimeForStopsBeforeArrivalTime(stopIds, arrivalTime, maxMinutesBeforehand));
    }

    public List<StopTime> getStopTimesByStopIdsAndTripIds(Set<Long> stopIds, Set<Long> tripIds) {
        return stopTimeParser.filter(stopTimeParser.getList(), new StopTimeByStopIdsAndTripIds(stopIds, tripIds));
    }

    public List<StopTime> getStopTimes(long stopId) {
        return stopTimeParser.filter(stopTimeParser.getList(), new StopTimeByStopId(stopId));
    }

    public Stop getStopById(long id) {
        List<Stop> stops = stopParser.filter(stopParser.getList(), new StopById(id));

        if (stops.size() > 1) {
            throw new IllegalStateException("Multiple stops found for id:" + id);
        } else if (stops.size() == 1) {
            return stops.get(0);
        } else {
            return null;
        }
    }

    public List<Stop> getStopsByIds(Set<Long> stopIds) {
        return stopParser.filter(stopParser.getList(), new StopByIds(stopIds));
    }

    public Trip getTripById(long id) {
        List<Trip> trips = tripParser.filter(tripParser.getList(), new TripById(id));

        if (trips.size() > 1) {
            throw new IllegalStateException("Multiple trips found for id:" + id);
        } else if (trips.size() == 1) {
            return trips.get(0);
        } else {
            return null;
        }
    }

    public List<Trip> getTripByRouteIds(String... routeIds) {
        List<Trip> trips = tripParser.filter(tripParser.getList(), new TripByRouteIds(routeIds));

        return trips;
    }

    public Route getRouteById(String id) {
        List<Route> routes = routeParser.filter(routeParser.getList(), new RouteById(id));

        if (routes.size() > 1) {
            throw new IllegalStateException("Multiple route found for id:" + id);
        } else if (routes.size() == 1) {
            return routes.get(0);
        } else {
            return null;
        }
    }

    private void load() throws IOException {
    	//XXX GTFS file Stream<...> that produces String[] rows?
    	//XXX parser is more of a  "collector/consumer" with a mapper.
        agencyParser.parseFile(rootDir + "/agency.txt");
        tripParser.parseFile(rootDir + "/trips.txt");
        stopParser.parseFile(rootDir + "/stops.txt");
        routeParser.parseFile(rootDir + "/routes.txt");
        //		calendarParser.parseFile(rootDir + "/calendar.txt");
        stopTimeParser.parseFile(rootDir + "/stop_times.txt");
    }

    public void show() {
        show(agencyParser.getList());
        show(tripParser.getList());
        show(stopParser.getList());
        show(routeParser.getList());
        //		show(calendarParser.getList());
        show(stopTimeParser.getList());
    }

    private void show(List<?> list) {
        System.out.println(list.getClass() + ": size=" + list.size());
    }

    public List<Stop> getStopByCode(String code) {
        return stopParser.filter(stopParser.getList(), new StopByCode(code));
    }

    public List<Stop> getStopsNearLocation(double lat, double lon, double dist) {
        return stopParser.filter(new StopByLatLon(lat, lon, dist));
    }
}
